#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <sys/statfs.h>
#include <sys/vfs.h>
#include <sys/mount.h>
#include <sys/wait.h>
/** arm-hismall-linux-gcc cross-tool doesnot has this header*/
//#include <linux/magic.h>

#include "debug.h"
#include "sdmng.h"
#include "record.h"

#ifndef MSDOS_SUPER_MAGIC
#define MSDOS_SUPER_MAGIC     0x4d44
#endif
#ifndef EXT3_SUPER_MAGIC
#define EXT3_SUPER_MAGIC      0xEF53
#endif
#ifndef MNT_DETACH
#define MNT_DETACH	0x02
#endif

#define SD_MIN_SIZE	300 //M
#define SD_EXIST_SIZE	200 //M

static st_rsc_dev_meta sd_meta;
static int storage_run = 1;
static int storage_del_flag = 0;
static int storage_del_size = 0;
static int storage_del_devid = 0;
static unsigned int storage_device_mask = 0; /// device broken mask
static pthread_mutex_t del_flag_mutex = PTHREAD_MUTEX_INITIALIZER;
static int dev_busy_flag[MAX_SD_DEV_NUM] = {0};/** determine device is being formatted or checked */
static pthread_mutex_t dev_busy_mutex = PTHREAD_MUTEX_INITIALIZER;

static int get_dev_node(int devid, char *node, int len)
{
    char dev_part[32]={0},device[32]={0};

    if(sd_meta.dev_subtype == DEV_SUB_SDIO)
    {
	snprintf(device,sizeof(device),"/dev/mmcblk%d",devid);
	snprintf(dev_part,sizeof(dev_part),"/dev/mmcblk%dp1",devid);
    }
    else
    {
	snprintf(device,sizeof(device),"/dev/sd%c",devid+'a');
	snprintf(dev_part,sizeof(dev_part),"/dev/sd%c1",devid+'a');
    }

    memset(node,0,len);
    if(len < strlen(dev_part))
	return -1;

    if(!(access(dev_part, 0))) // first use partition
	strcpy(node,dev_part);
    else if(!(access(device, 0)))
	strcpy(node,device);

    return 0;
}

static int rec_dir_filter(const struct dirent *entry)
{
    if (!strncmp(entry->d_name, ".",1))
	return 0;
    else if(strlen(entry->d_name)!=strlen("20141111"))
	return 0;
    else 
    {
	int i;
	for(i=0;i<8;i++)
	{
	    if(entry->d_name[i]<'0' || entry->d_name[i]>'9')
		return 0;
	}
    }
    return 1;
}
static int rec_file_filter(const struct dirent *entry)
{
    if (!strncmp(entry->d_name, ".",1))
	return 0;
    else if(strlen(entry->d_name)!=strlen("111111.mav"))
	return 0;
    else if(strncmp(entry->d_name+6,".mav",4)!=0)
	return 0;
    else 
    {
	int i;
	for(i=0;i<6;i++)
	{
	    if(entry->d_name[i]<'0' || entry->d_name[i]>'9')
		return 0;
	}
    }
    return 1;
}
static int find_oldest_dir(int devid,char *dir,int len)
{
    int ret = 0,dirnum = 0,cnt = 0;
    struct dirent **dirlist = NULL;
    char mpoint[64]={0};
    struct stat ststat;

    memset(dir,0,len);
    rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint));

    dirnum = scandir(mpoint, &dirlist, rec_dir_filter, alphasort);
    if (dirnum < 0) 
    {
	SLOG(ERR, "scandir %s error and dirnum = %d\n",mpoint, dirnum);
	return -1;
    }
    else if(dirnum == 1)
    {
	strncpy(dir,dirlist[0]->d_name,len);
	free(dirlist[0]);
	free(dirlist);
	return 0;
    }

    char item_name[128]={0};
    for (cnt=0; cnt < dirnum; cnt++) 
    {
	memset(&ststat,0,sizeof(struct stat));
	snprintf(item_name,sizeof(item_name),"%s/%s",mpoint,dirlist[cnt]->d_name);
	ret = stat(item_name, &ststat);
	if (ret < 0) 
	{
	    SLOG(ERR, "get stat %s error:%s\n", item_name,strerror(errno));
	    continue;//break;
	} 
	else if (S_ISDIR(ststat.st_mode)&&sd_meta.filter(item_name,&ststat)) 
	{
	    strncpy(dir,dirlist[cnt]->d_name,len);
	    break;
	}
    }

    /** ensure dir is valid */
    if(strlen(dir)==0)
	strncpy(dir,dirlist[0]->d_name,len);

    for (cnt=0; cnt < dirnum; cnt++) 
    {
	free(dirlist[cnt]);
    }

    free(dirlist);

    return ret;
}

static int find_oldest_file(int devid,const char *dir,char *file,int len)
{
    int ret = 0,filenum = 0,cnt = 0;
    struct dirent **filelist = NULL;
    char mpoint[64]={0},path[128]={0};
    struct stat ststat;

    memset(file,0,len);
    rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint));
    snprintf(path,sizeof(path),"%s/%s",mpoint,dir);

    filenum = scandir(path, &filelist, rec_file_filter, alphasort);
    if (filenum < 0) 
    {
	SLOG(ERR, "scandir %s error and filenum = %d\n",path, filenum);
	return -1;
    }

    char item_name[128]={0};
    for (cnt=0; cnt < filenum; cnt++) 
    {
	memset(&ststat,0,sizeof(struct stat));
	snprintf(item_name,sizeof(item_name),"%s/%s",path,filelist[cnt]->d_name);
	ret = stat(item_name, &ststat);
	if (ret < 0) 
	{
	    SLOG(ERR, "get stat %s error:%s\n", item_name,strerror(errno));
	    continue;//break;
	} 
	else if (S_ISREG(ststat.st_mode)&&sd_meta.filter(item_name,&ststat)) 
	{
	    strncpy(file,filelist[cnt]->d_name,len);
	    break;
	}
    }

    for (cnt=0; cnt < filenum; cnt++) 
    {
	free(filelist[cnt]);
    }

    free(filelist);

    return ret;
}

static int find_oldest_file_device(int devnum, int devmask)
{
    int ret = 0,i = 0, devid = 0;
    char old_dir[64]={0};
    char oldest_dir[64]={0};

    if(devnum < 0 || devmask < 0)
    {
	SLOG(ERR,"invalid devnum[%d] or devmask[%d]\n",devnum,devmask);
	return -1;
    }
    if(devmask == (1<<devnum)-1)
    {
	SLOG(ERR,"all[%d] sd is broken,devmask[0x%x]!\n",devnum,devmask);
	return -1;
    }
    for(i=0;i<devnum;i++)
    {
	if((devmask>>i)&0x01) /// it's a broken device
	    continue;
	ret = find_oldest_dir(i,old_dir,sizeof(old_dir));
	if(ret < 0)
	    continue;
	SLOG(PRI,"the oldest dir[%s] found in device[%d].\n",old_dir,i);
	if((strlen(oldest_dir)==0)||(strcmp(oldest_dir,old_dir)>0))
	{
	    strcpy(oldest_dir,old_dir);
	    devid = i;
	}
	else if(strcmp(oldest_dir,old_dir)==0)
	{// name is same, find oldest file in them
	    char oldest_file[64]={0},old_file[64]={0};
	    ret = find_oldest_file(devid,oldest_dir,oldest_file,sizeof(oldest_file));
	    if(ret < 0)	continue;
	    ret = find_oldest_file(i,old_dir,old_file,sizeof(old_file));
	    if(ret < 0)	continue;
	    if(strcmp(oldest_file,old_file)>0)
		devid = i;
	}
    }
    return devid;
}

/**
 * delete oldest file in the dir
 * @param[in] size the need to free size,MB
 * @param[in] dir the directory to delete files(abs path)
 * @ret return the size that has freed,or zero if fail
 */
static int rm_in_sd_file(int size, char *dir)
{
    int ret;
    int clearsize = 0;
    struct stat ststat;
    struct dirent **dirlist;
    int cnt = 0, file_num;

    file_num = scandir(dir, &dirlist, 0, alphasort);
    if (file_num < 0) {
	SLOG(ERR, "scandir %s error and file_num: %d\n", dir, file_num);
	return 0;
    } 
    else if (2 == file_num) {
	remove(dir); // remove empty folder
	SLOG(PRI,"remove [%s] empty dir\n",dir);
	clearsize = 0;
    }
    else 
    {
	char item_name[128]={0};
	for (cnt=0; cnt < file_num; cnt++) 
	{
	    snprintf(item_name,sizeof(item_name),"%s/%s",dir,dirlist[cnt]->d_name);
	    if (!strcmp(dirlist[cnt]->d_name, ".")||!strcmp(dirlist[cnt]->d_name, ".."))
		continue;
	    //SLOG(PRI,"item_name is %s,clearsize = %d,sd_meta.delter=%p\n",
	    //		item_name,clearsize,sd_meta.delter);
	    ret = stat(item_name, &ststat);
	    if (ret < 0) 
	    {
		SLOG(ERR, "stat %s error!\n", item_name);
		continue;//break;
	    }
	    if(clearsize >= size)
		break;
	    if (S_ISDIR(ststat.st_mode)) 
	    {// this should not happen so far 
		size -= clearsize;
		clearsize += rm_in_sd_file(size,item_name);
	    } 
	    else if(!sd_meta.delter(item_name,&ststat)) 
	    {
		clearsize += ststat.st_size>>20;
	    }
	}
    }

    /// to remove the empty directory now
    if(cnt == file_num)
    {
	ret = remove(dir);/// remove only can delete empty dir
	SLOG(PRI,"remove [%s] empty dir,ret = %d\n",dir,ret);
    }

    for (cnt=0; cnt<file_num; cnt++) 
	free(dirlist[cnt]);

    free(dirlist);

    return clearsize;
}

/* 
 * use scandir search dir or fill by seq
 * scandir will malloc, so must free in end
 * @ret return the size freed by deleteing files
 */
static long long free_sd_space(int devid, int size)
{
    int ret;
    long long clearsize = 0;
    struct stat ststat;
    struct dirent **name_list;
    int cnt, file_num;
    char mpoint[64]={0};

    rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint));

    file_num = scandir(mpoint, &name_list, rec_dir_filter, alphasort);
    if (file_num < 0) 
    {
	SLOG(ERR, "scandir %s error and file_num = %d\n",mpoint, file_num);
	return 0;
    }
    //SLOG(PRI, "scandir %s success and file_num:%d\n", sd_meta.mpoint, file_num);

    char item_name[128]={0};
    for (cnt=0; cnt < file_num; cnt++) 
    {
	snprintf(item_name,sizeof(item_name),"%s/%s",mpoint,name_list[cnt]->d_name);
	memset(&ststat,0,sizeof(struct stat));
	ret = stat(item_name, &ststat);
	if (ret < 0) 
	{
	    SLOG(ERR, "get stat %s error:%s\n", item_name,strerror(errno));
	    continue;//break;
	} 
	if(clearsize >= size)
	    break;
	else if (S_ISDIR(ststat.st_mode)) 
	{
	    size -= clearsize;
	    clearsize += rm_in_sd_file(size,item_name);
	    SLOG(PRI,"rm [%s] get [%d]M size\n",item_name,clearsize);
	    /// avoid delete newer file than another card files
	    /// but may don't get enough free space for need,
	    /// luckily we have reserved exist space. 
	    if(sd_meta.max_num > 1) 
		break;
	}
    }

    for (cnt=0; cnt < file_num; cnt++) 
    {
	free(name_list[cnt]);
    }

    free(name_list);

    return clearsize;
}

int rsc_get_file_list(const char *path,struct sd_item_list **list)
{
    int ret = 0;
    int i,file_num = 0;
    struct dirent **dirlist = NULL;
    char mpoint[64]={0};
    char abs_path[128]={0};

    if(rsc_get_sd_mpoint(0,mpoint,sizeof(mpoint))<0)
    {
	SLOG(ERR,"rsc_get_sd_mpoint sd[0] fail!\n");
	return -1;
    }
    if(NULL==path)
    {
	snprintf(abs_path,sizeof(abs_path),"%s",mpoint);
	file_num = scandir(abs_path, &dirlist, rec_dir_filter, alphasort);
    }
    else
    {
	snprintf(abs_path,sizeof(abs_path),"%s/%s",mpoint,path);
	file_num = scandir(abs_path, &dirlist, rec_file_filter, alphasort);
    }

    if (file_num < 0) 
    {
	SLOG(ERR, "scandir %s error and file_num: %d\n", abs_path, file_num);
	return -1;
    } 
    else if (file_num == 0)
    {/* need to free dirlist */
	SLOG(PRI, "dir %s is empty.\n", abs_path);
	ret = -1;
    }
    else 
    {
	int list_cnt = 0;
	struct stat ststat;
	struct sd_item_list *plist = 0;
	char item_name[128]={0};

	SLOG(PRI, "scandir %s success and file_num: %d.\n", abs_path, file_num);
	plist = calloc(1,sizeof(struct sd_item_list)+file_num*sizeof(struct file_item_info));
	if (plist == NULL) 
	{
	    SLOG(ERR, "malloc sd file list error!\n");
	    ret = -1;
	    goto end;
	}

	for (i=0; i < file_num; i++) 
	{
	    snprintf(item_name,sizeof(item_name),"%s/%s",abs_path,dirlist[i]->d_name);
	    memset(&ststat,0,sizeof(struct stat));
	    ret = stat(item_name, &ststat);
	    if (ret < 0) 
	    {
		SLOG(ERR, "stat %s error.\n", item_name);
		continue;
	    }
	    else if (!strncmp(dirlist[i]->d_name, ".",1)) // ignore hidden dir or file
		continue;
	    else if (S_ISDIR(ststat.st_mode)) 
	    {
		plist->item[list_cnt].type = TYPE_SD_DIR;
		strncpy(plist->item[list_cnt].fname, dirlist[i]->d_name, strlen(dirlist[i]->d_name));
		plist->item[list_cnt].fsize = ststat.st_size;
		list_cnt++;
	    } 
	    else
	    {
		//if(strstr(dirlist[i]->d_name, REC_SUFFIX)){;}
		plist->item[list_cnt].type = TYPE_SD_FILE;
		strncpy(plist->item[list_cnt].fname, dirlist[i]->d_name, strlen(dirlist[i]->d_name));
		plist->item[list_cnt].fsize = ststat.st_size;
		list_cnt++;
	    }
	}
	if(list_cnt==0)
	{
	    free(plist);
	    plist = NULL;
	    ret = -1;
	}
	else
	{
	    plist->cnt = list_cnt;
	    *list = plist;
	}
    }

end:
    for (i=0; i<file_num; i++) 
	free(dirlist[i]);
    if(dirlist)
	free(dirlist);

    return ret;
}

int rsc_del_file(const char *path)
{
    int ret = 0;
    char mpoint[64]={0};
    char abs_path[128]={0};

    if(rsc_get_sd_mpoint(0,mpoint,sizeof(mpoint))<0)
    {
	SLOG(ERR,"rsc_get_sd_mpoint sd[0] fail!\n");
	return -1;
    }
    if(NULL==path)
	snprintf(abs_path,sizeof(abs_path),"%s",mpoint);
    else
	snprintf(abs_path,sizeof(abs_path),"%s/%s",mpoint,path);

    struct stat ststat;
    if(stat(abs_path,&ststat))
    {
	SLOG(ERR,"stat %s fail!\n",abs_path);
	return -1;
    }
    else if(S_ISDIR(ststat.st_mode))
    {
	char cmd[32] = {0};
	snprintf(cmd,sizeof(cmd), "rm -fr %s/*", abs_path);
	ret = system(cmd);
    }
    else 
    {
	ret = remove(abs_path);
    }

    if(ret) ret = -1;
    return ret;
}

int rsc_mount_device(int devid)
{
    int ret = 0;
    struct statfs st_fs;
    char sd_devnode[32]={0};
    char mpoint[64]={0};

    if(rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint))<0)
    {
	SLOG(ERR,"rsc_get_sd_mpoint [id:%d] error!\n",devid);
	return -1;
    }
    if(rsc_get_dev_busy(devid))
    {
	SLOG(WRN,"device [%d] is being formatted or checked!\n",devid);
	return -1;
    }
    get_dev_node(devid,sd_devnode,sizeof(sd_devnode));
    if (strlen(sd_devnode))	
    {
	SLOG(DBG, "sd[%d] exist.\n",devid);
	ret = statfs(mpoint, &st_fs);
	if (ret < 0)
	{
	    SLOG(ERR, "statfs %s err\n", mpoint);
	    return -1;
	}

	if ((sd_meta.fs_type==FS_VFAT) && (st_fs.f_type != MSDOS_SUPER_MAGIC))
	{
	    ret = mount(sd_devnode, mpoint, "vfat", 0, "shortname=mixed");
	    if (ret!=0)
	    {/// ENOMEDIUM(123)
		ret = (errno>0)?(-1)*errno:errno;
		SLOG(ERR, "mount %s err[%d]:%s.\n", mpoint,errno,strerror(errno));
		return ret;
	    }
	    SLOG(PRI, "mount %s to %s success.\n", sd_devnode, mpoint);
	}
	else if ((sd_meta.fs_type==FS_EXT3) && (st_fs.f_type != EXT3_SUPER_MAGIC))
	{
	    ret = mount(sd_devnode, mpoint, "ext3", 0, NULL);
	    if (ret!=0)
	    {
		ret = (errno>0)?(-1)*errno:errno;
		SLOG(ERR, "mount %s err[%d]:%s.\n", mpoint,errno,strerror(errno));
		return ret;
	    }
	    SLOG(PRI, "mount %s to %s success.\n", sd_devnode, mpoint);
	}
    }
    else
    {
	SLOG(ERR, "sd[%d] does not exist.\n",devid);
	return -1;
    }

    // sd card is prepared
    rsc_rec_setpenv(1);
    return 0;
}

int rsc_umount_device(int devid)
{
    int ret = 0;
    struct statfs st_fs;
    char sd_devnode[32]={0};
    char mpoint[64]={0};

    if(rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint))<0)
    {
	SLOG(ERR,"rsc_get_sd_mpoint [id:%d] error!\n",devid);
	return -1;
    }
    get_dev_node(devid,sd_devnode,sizeof(sd_devnode));
    if (strlen(sd_devnode))	
    {
	SLOG(DBG, "sd[%d] exist.\n",devid);

	ret = statfs(mpoint, &st_fs);
	if (ret < 0)
	{
	    SLOG(ERR, "statfs %s err\n", mpoint);
	    return -1;
	}

	if (st_fs.f_type == MSDOS_SUPER_MAGIC || st_fs.f_type == EXT3_SUPER_MAGIC)
	{
#ifdef MNT_DETACH
	    ret = umount2(mpoint,MNT_DETACH);
#else
	    char cmd[64]={0};
	    snprintf(cmd,sizeof(cmd),"umount -l %s\n",mpoint);
	    /// system has a blocked risk in uclibc-0.9.28,should avoid use it.
	    ret = system(cmd);
#endif
	    if (ret != 0)
	    {
		SLOG(ERR, "umount %s err,ret = %d\n", mpoint,ret);
		return -1;
	    }
	    SLOG(DBG, "umount %s lazy success.\n",mpoint);
	    /// check device has been unmounted really
	    int cnt = 30;
	    while(cnt--)
	    {
		ret = statfs(mpoint, &st_fs);
		if (ret < 0)
		{
		    SLOG(ERR, "statfs %s err:%s\n", mpoint,strerror(errno));
		    return -1;
		}
		else if(st_fs.f_type == MSDOS_SUPER_MAGIC || st_fs.f_type == EXT3_SUPER_MAGIC)
		{
		    sleep(1);
		    continue;
		}
		else
		    break;
	    }
	    SLOG(DBG, "umount %s real success.\n",mpoint);
	}
	else
	{
	    SLOG(WRN, "sd[%d] may not be mounted,f_type:0x%x\n",devid, st_fs.f_type);
	    return 0;
	}
    }
    else
    {
	SLOG(ERR, "sd[%d] does not exist.\n",devid);
	return -1;
    }

    return 0;
}

int rsc_format_device(int devid)
{
    int ret;

    SLOG(PRI, "start to format sd[%d]......\n",devid);
    rsc_set_dev_busy(devid,RSC_FORMAT_FLAG);
    if(rsc_umount_device(devid)<0)
    {
	rsc_set_dev_busy(devid,0);
	return -1;
    }
    char cmd[32]={0},devnode[32]={0};
    get_dev_node(devid,devnode,sizeof(devnode));
    if(sd_meta.fs_type == FS_VFAT)
    {
	snprintf(cmd,sizeof(cmd),"mkdosfs -I -F 32 %s",devnode);
    }
    else if(sd_meta.fs_type == FS_EXT3)
    {
	snprintf(cmd,sizeof(cmd),"mkfs.ext3 %s",devnode);
    }
    ret = system(cmd);
    if(ret != 0)
    {
	SLOG(ERR,"system error!\n");
	rsc_set_dev_busy(devid,0);
	return -1;
    }
    SLOG(PRI,"format sd[%d] success!\n",devid);

    rsc_set_dev_busy(devid,0);
    if(rsc_mount_device(devid) < 0)
	return -2;

    return 0;
}

int rsc_get_sd_mpoint(int devid, char *mpoint,int len)
{
    if(devid < 0|| devid >=sd_meta.max_num || mpoint==NULL || len <= 10)
    {
	SLOG(ERR,"invalid params:devid[%d],max_num[%d],len[%d]!\n",devid,sd_meta.max_num,len);
	return -1;
    }
    snprintf(mpoint,len,"%s/sd%d",sd_meta.mpoint, devid+1);
    if(access(mpoint,0))
    {
	mkdir(mpoint,777);
    }
    return 0;
}

int rsc_get_sd_devid(const char *mpoint)
{
    if(mpoint==NULL)
    {
	SLOG(ERR,"mpoint is NULL!\n");
	return -1;
    }
    int len = strlen(sd_meta.mpoint);
    if(strncmp(mpoint,sd_meta.mpoint,len))
    {
	SLOG(ERR,"mpoint = %s,sd_meta.mpoint = %s!\n",mpoint,sd_meta.mpoint);
	return -1;
    }
    int id = 0,ret = 0;
    ret = sscanf(mpoint,"/%*[^/]/sd%d",&id);
    SLOG(PRI,"ret = %d, mpoint = %s, id = %d\n",ret, mpoint,id);

    return (ret==1)?id-1:-1;
}

/**
 * set loop-delete flag and info.
 */
int rsc_set_loop_delete(int devid,int size)
{
    pthread_mutex_lock(&del_flag_mutex);
    storage_del_flag = 1;
    storage_del_size = size;
    storage_del_devid = devid;
    pthread_mutex_unlock(&del_flag_mutex);
    return 0;
}

/*
 * get available device to record
 * @param[in] size the size need to use,unit is MB
 * @param[in,out] mpoint the device mount point
 * @ret if fail,return -1;if success,return 0
 */
static pthread_mutex_t dev_resource_lock = PTHREAD_MUTEX_INITIALIZER;
int rsc_get_avail_dev(int size, char *mpoint)
{
    long long freespace = 0;
    struct sd_info info;
    int devid = sd_meta.prev_devid;
    int i = 0,ret = 0;
    unsigned int dev_mask = 0;/// mask broken device

    static struct dev_resource_req
    {
	int psize;
	int pret;
	time_t ptime;
	char pmpoint[64];
    } prev_req;

    if(mpoint == NULL)
    {
	SLOG(ERR,"passed-mpoint is NULL!\n");
	return -1;
    }

    pthread_mutex_lock(&dev_resource_lock);
    int time_diff = time(NULL)-prev_req.ptime;
    if(time_diff>=0 && time_diff<5 && size <= prev_req.psize)
    {
	ret = prev_req.pret;
	if(ret==0)
	    strcpy(mpoint,prev_req.pmpoint);
	//SLOG(PRI,"time_diff = %d(0~5),use previous avail dev,mpoint = %s!\n",
	//		time_diff,ret?"null":mpoint);
	pthread_mutex_unlock(&dev_resource_lock);
	return ret;
    }
    if(size < 0) size = 1; /// ensure size > 0
    size += SD_MIN_SIZE;// min free space to ensure
    prev_req.psize = size;

    /// firstly,we find available sd card that has enough freespace.
    //SLOG(PRI,"start to find available sd card from sd[%d]...\n",devid);
    for(i=0;i<sd_meta.max_num;i++)
    {
	memset(&info,0,sizeof(info));
	ret = rsc_get_sd_info(devid,&info);
	devid = (devid+1)%sd_meta.max_num;
	if(ret < 0)
	{
	    //SLOG(ERR,"get sd[%d] info error,status = %d\n",info.dev_id,info.status);
	    dev_mask |= (1<<info.dev_id);
	    continue;
	}
	if(info.status == EXIST_READ_ONLY)
	{
	    SLOG(WRN,"sd[%d] status[%d] is RDONLY.\n",info.dev_id,info.status);
	    dev_mask |= (1<<info.dev_id);
	    continue;
	}

	freespace = info.freespace;
	SLOG(DBG,"sd[%d] free vol = %lu\n",info.dev_id,freespace);

	if(freespace >= size)
	{// here we find it, and update last recently used device ID.
	    strcpy(mpoint,info.mpoint);
	    sd_meta.prev_devid = info.dev_id;
	    ret = 0;
	    goto avail_dev_end;
	}
    }
    storage_device_mask = dev_mask;
    if(dev_mask==(1<<sd_meta.max_num)-1)
    {
	//SLOG(ERR,"all sd card is not read-write for record!\n");
	ret = -1;
	goto avail_dev_end;
    }

    memset(&info,0,sizeof(info));
    /// secondly, we need to loop delete files to free space
    //  1. find the oldest files in which sd card.
    devid = find_oldest_file_device(sd_meta.max_num,dev_mask);
    ret = rsc_get_sd_info(devid,&info);
    //  2. delete them and update freespace info.
    if(ret==0 && info.status!=EXIST_READ_ONLY)	
    {
	size -= info.freespace;
	SLOG(PRI,"need to delete oldest file in[%d] to free [%d]MB space!\n",devid,size);
	/// start loop-delete process 
	rsc_set_loop_delete(devid,size);

	strcpy(mpoint,info.mpoint);
	sd_meta.prev_devid = info.dev_id;

	ret = 0;
	goto avail_dev_end;
    }
    ret = -1;

avail_dev_end:
    prev_req.ptime = time(NULL);
    prev_req.pret = ret;
    if(ret==0)
	strcpy(prev_req.pmpoint,mpoint);
    pthread_mutex_unlock(&dev_resource_lock);

    return ret;
}

int rsc_get_sd_info(int devid, struct sd_info *info)
{
    int ret;
    struct statfs st_fs;
    char devnode[32]={0};
    char mpoint[64]={0};

    if(devid < 0|| devid >=sd_meta.max_num || info == NULL)
    {
	SLOG(ERR,"invalid params,devid = %d!\n",devid);
	return -1;
    }
    info->dev_id = devid;
    if(rsc_get_sd_mpoint(devid,mpoint,sizeof(mpoint))<0)
    {
	SLOG(ERR,"rsc_get_sd_mpoint [id:%d] error!\n",devid);
	return -1;
    }
    get_dev_node(devid,devnode,sizeof(devnode));

    static int dev_stat[10]={0};/// status: 1 - not exist, 0 - exist
    if (!strlen(devnode))
    {
	if(dev_stat[devid])	;/// don't print error that has printed
	else
	{
	    SLOG(ERR, "sd[%d] does not exist\n",devid);
	    dev_stat[devid] = 1;
	}
	//printf(">>>>>>>>111 no exist!\n");
	info->status = NO_EXIST;
	return -1;
    }
    else 
    {
	ret = statfs(mpoint, &st_fs);
	if (ret < 0)
	{
	    SLOG(ERR, "statfs %s err\n", mpoint);
	    info->status = EXIST_ILLEGAL;
	    return -1;
	}

	if (st_fs.f_type == MSDOS_SUPER_MAGIC || st_fs.f_type == EXT3_SUPER_MAGIC) 
	{
	    dev_stat[devid] = 0; /// suppose we have sd card
	    strcpy(info->devname, devnode);
	    strcpy(info->mpoint, mpoint);
	    info->totalspace = ((unsigned long long)st_fs.f_blocks*st_fs.f_bsize)>>20; //M
	    info->freespace = ((unsigned long long)st_fs.f_bfree*st_fs.f_bsize)>>20;  //M

	    if (info->freespace > SD_EXIST_SIZE)
	    {
		char test_file[64]={0};
		snprintf(test_file,sizeof(test_file),"%s/.test",mpoint);
		ret = open(test_file, O_CREAT|O_WRONLY|O_TRUNC, 0777);
		if (ret < 0)
		{
		    SLOG(ERR, "creat test file err:%s!\n",strerror(errno));
		    info->status = EXIST_READ_ONLY;
		    rsc_set_dev_busy(devid,RSC_FCHECK_FLAG);
		}
		else
		{
		    SLOG(DBG, "sd[%d] read write success.\n",devid);
		    info->status = EXIST_RDWR_OK;
		    close(ret);
		    remove(test_file);
		}
	    }
	    else
	    {
		SLOG(WRN, "sd[%d] space %dM less exist size!\n",devid,info->freespace);
		info->status = EXIST_LESS_SPC;
	    }
	}
	else
	{
	    //if(dev_stat[devid] || rsc_mount_device(devid) == -ENOMEDIUM)
	    if(rsc_mount_device(devid) == -ENOMEDIUM) /* hot-plug check for sdio */
	    {
		//if(dev_stat[devid])	;
		//else
		SLOG(ERR, "sd[%d] does not exist\n",devid);
		dev_stat[devid] = 1;
		info->status = NO_EXIST;
	    }
	    else
	    {
		SLOG(ERR, "sd[%d] is not mounted,but has try mount it!\n",devid);
		info->status = EXIST_NOMOUNT;
	    }
	    return -1;
	}
    }

    return 0;
}

static int check_mpoint_valid(void)
{
    int i = 0;
    char mpoint[64]={0};
    struct statfs stfs;

    for(i=0;i<sd_meta.max_num;i++)
    {
	memset(mpoint,0,sizeof(mpoint));
	memset(&stfs,0,sizeof(stfs));
	rsc_get_sd_mpoint(i,mpoint,sizeof(mpoint));
	if(strncmp(mpoint,"/mnt/sd",strlen("/mnt/sd")))
	{
	    SLOG(WRN,"mpoint[%s] is not valid!\n",mpoint);
	}
	else if(statfs(mpoint,&stfs))
	{
	    SLOG(ERR,"statfs %s error!\n",mpoint);
	}
	else if(stfs.f_type!= MSDOS_SUPER_MAGIC || 
		((unsigned long)(stfs.f_blocks*stfs.f_bsize)>>20) < SD_EXIST_SIZE)
	{
	    //SLOG(WRN,"mpoint[%s] is invalid now!\n",mpoint);
	    if(rsc_rec_isrecod())
	    {
		rsc_chn_stop(0);
		// remove all files on the mpoint to avoid write on flash
		char cmd[128]={0};
		snprintf(cmd,sizeof(cmd),"rm -rf %s/*",mpoint);
		system(cmd);
		rsc_chn_start(0);
	    }
	}
    }
    return 0;
}
void *rsc_storage_process(void *arg)
{
    int ret = 0;
    int del_flag = 0;
    int del_size = 0;
    int del_devid = -1;
    int clearsize = 0;
    struct sd_info info;

    while(storage_run)
    {
	check_mpoint_valid();

	pthread_mutex_lock(&del_flag_mutex);
	del_flag = storage_del_flag;
	del_size = storage_del_size;
	if(del_flag) /// keep del_devid if no del req
	    del_devid = storage_del_devid;
	/// reset delete flag and delete size
	storage_del_flag = 0;
	storage_del_size = 0;
	pthread_mutex_unlock(&del_flag_mutex);
	if(del_flag && del_size > 0)
	{
	    clearsize = free_sd_space(del_devid,del_size);
	    if(clearsize < del_size)
	    {
		SLOG(WRN,"only get space [%d]MB < need [%d]MB\n",clearsize,del_size);
		ret = -1;
	    }
	    SLOG(PRI,"[ReqDel]delete files in dev[%d] free [%d]MB space!\n",del_devid,clearsize);
	}
	else if(sd_meta.max_num > 1 && del_devid >= 0 
		&& storage_device_mask!=(1<<sd_meta.max_num)-1)
	{/// only multi-card solution need this deal
	    int devid = 0;
	    memset(&info,0,sizeof(info));
	    ret = rsc_get_sd_info(del_devid,&info);
	    if(ret==0 && info.status == EXIST_LESS_SPC)
	    {
		devid = find_oldest_file_device(sd_meta.max_num,storage_device_mask);
		if(devid >= 0 && devid!=del_devid)
		{/// restart record on another sd card
		    SLOG(PRI,"need to switch rec from sd[%d] to sd[%d]\n",del_devid,devid);
//		    rsc_stop_record(RSC_REC_MAX_CHN);
		    del_devid = devid;
		    sd_meta.prev_devid = devid;
		    clearsize = free_sd_space(del_devid,SD_EXIST_SIZE);
		    SLOG(PRI,"[ExistDel]delete files in dev[%d] free [%d]MB space!\n",del_devid,clearsize);
		    sleep(1);/// wait record stop 
//		    rsc_start_record(RSC_REC_MAX_CHN);
		}
		else
		{/// loop delete continue
		    clearsize = free_sd_space(del_devid,SD_MIN_SIZE);
		    SLOG(PRI,"[MinDel]delete files in dev[%d] free [%d]MB space!\n",del_devid,clearsize);
		}
		continue;
	    }
	    sleep(1);
	}
	else
	{
	    sleep(1);
	}
    }
    return (void *)NULL;
}

static void rsc_dosfsck_func(int devid)
{
    int i = devid;
    char devnode[32]={0};
    if(rsc_umount_device(i)<0)
    {
	SLOG(ERR,"rsc_umount_device [%d] fail!\n",i);
	rsc_set_dev_busy(i,0);
    }
    else if(get_dev_node(i,devnode,sizeof(devnode))<0)
    {
	SLOG(ERR,"get device [%d] node fail!\n",i);
	rsc_set_dev_busy(i,0);
    }
    else
    {
	char cmd[64]={0};
	snprintf(cmd,sizeof(cmd),"%s -aw %s",
		(sd_meta.fs_type==FS_VFAT)?"dosfsck":"fsck",devnode);
	system(cmd);
	SLOG(PRI,"we finish dosfsck !!!\n");
	rsc_set_dev_busy(i,0);
	rsc_mount_device(i);
    }
}

int rsc_init_storage(st_rsc_dev_meta *dmeta)
{
    if(dmeta == NULL)
    {
	SLOG(ERR,"invalid params!\n");
	return -1;
    }
    if((dmeta->dev_type != DEV_SD_TYPE) ||
	    (dmeta->fs_type > FS_MAX) || (dmeta->max_num > 4))
    {
	SLOG(ERR,"not supported params!\n");
	return -1;
    }
    if(dmeta->filter==NULL || dmeta->delter==NULL)
    {
	SLOG(ERR,"storage filter or delter callback is null!\n");
	return -1;
    }
    if(dmeta->prev_devid<0 || dmeta->prev_devid >= dmeta->max_num) 
	dmeta->prev_devid = 0;
    memcpy(&sd_meta,dmeta,sizeof(st_rsc_dev_meta));

    int i = 0;
    char mpoint[64]={0};
    for(i=0;i<sd_meta.max_num;i++)
    {
	snprintf(mpoint,sizeof(mpoint),"%s/sd%d",sd_meta.mpoint,i+1);
	mkdir(mpoint,0777);
	rsc_set_dev_busy(i,RSC_FCHECK_FLAG); /* check first before use it */
	rsc_dosfsck_func(i);
    }
    pthread_t fix_tid;
    int ret = pthread_create(&fix_tid,NULL,rsc_fix_dev_process,NULL);
    if(ret)
    {
	SLOG(ERR,"create rsc_fix_dev_process thread error:%s\n",strerror(ret));
	return -1;
    }
    pthread_detach(fix_tid);
    //rsc_mount_all();
    return 0;
}

int rsc_exit_storage(void)
{
    storage_run = 0;
    return 0;
}

int rsc_set_prev_devid(int devid)
{
    if(devid<0||devid>=sd_meta.max_num)
    {
	SLOG(ERR,"device id[%d] is invalid!\n",devid);
	return -1;
    }
    sd_meta.prev_devid = devid;
    return 0;
}

int rsc_mount_all(void)
{
    int i = 0;
    for(i=0;i<sd_meta.max_num;i++)
    {
	SLOG(PRI,"mount sd[%d] device...\n",i);
	rsc_mount_device(i);
    }
    return 0;
}

int rsc_umount_all(void)
{
    int i = 0;
    for(i=0;i<sd_meta.max_num;i++)
    {
	SLOG(PRI,"umount sd[%d] device...\n",i);
	rsc_umount_device(i);
    }

    return 0;
}

int rsc_get_dev_busy(int devid)
{
    int flag = 0;
    if(devid<0||devid>=MAX_SD_DEV_NUM)
	return flag;
    pthread_mutex_lock(&dev_busy_mutex);
    flag = dev_busy_flag[devid];
    pthread_mutex_unlock(&dev_busy_mutex);
    return flag;
}

int rsc_set_dev_busy(int devid,int flag)
{
    if(devid<0||devid>=MAX_SD_DEV_NUM)
	return 0;
    pthread_mutex_lock(&dev_busy_mutex);
    if(flag && dev_busy_flag[devid]) ;
    else dev_busy_flag[devid] = flag;
    pthread_mutex_unlock(&dev_busy_mutex);
    return 0;
}

void *rsc_fix_dev_process(void *arg)
{
    int devnum = sd_meta.max_num,i = 0;

    while(storage_run)
    {
	for(i=0;i<devnum;i++)
	{
	    if(rsc_get_dev_busy(i)==RSC_FCHECK_FLAG)
	    {/** need to fix device filesystem */
		rsc_dosfsck_func(i);
	    }
	}
	sleep(1);
    }

    return (void*)NULL;
}
